package com.oracle.coherence.patterns.security.accesscontrol;

import com.oracle.coherence.patterns.security.extend.CachePermission;
import com.oracle.coherence.patterns.security.extend.ExtendAccessController;
import com.oracle.coherence.patterns.security.extend.InvocationPermission;
import com.tangosol.run.xml.XmlElement;
import com.tangosol.run.xml.XmlHelper;

import javax.security.auth.Subject;

/**
 * This AccessController is used to secure Coherence cluster membership.
 * It uses Kerberos to authenticate and authorise access to Coherence.
 * <p/>
 * In order to implement the encrypt and decrypt methods the code needs
 * to work with java.securitySignedObject which requires the use of
 * public and private keys which are not available in Kerberos.
 * To work around this a Java keystore is used
 * which holds a dummy key pair. This keystore is then used by every
 * cache server instance. Coherence uses this to encrypt and decrypt the
 * permission request and also to verify the subject. The use of a common
 * (dummy) keystore is not seen as a risk as the permission request is not
 * sensitive and the Subject is verified by Kerberos in the checkPermission
 * method.
 */
public class ConfigurableExtendAccessController implements ExtendAccessController {

    /**
     * The permissions checker that will be used to verify permissions of subjects
     */
    private PermissionsChecker permissionsChecker;

    /**
     * The XML configuration used to configure this access controller
     */
    private XmlElement xmlConfig;

    /**
     * Empty constructor used in testing.
     */
    public ConfigurableExtendAccessController() {
    }

    /**
     * Create a new ConfigurableExtendAccessController.
     * </p>
     *
     * @param xmlConfig - the configuration of this access controller
     */
    public ConfigurableExtendAccessController(XmlElement xmlConfig) {
        setConfig(xmlConfig);
    }

    PermissionsChecker getPermissionsChecker() {
        return permissionsChecker;
    }

    void setPermissionsChecker(PermissionsChecker permissionsChecker) {
        this.permissionsChecker = permissionsChecker;
    }

    /**
     * This method performs the access control checks for the
     * specified <code>CachePermission</code> and <code>Subject</code>.
     * <p/>
     * If the check fails then a <code>java.lang.SecurityException</code>
     * will be thrown.
     * <p/>
     * <b>Note: </b>This method <i>must</i> be implemented to be thread safe.
     * <p/>
     *
     * @param permission The requested permission
     * @param subject    The subject accessing the cache
     * @throws SecurityException if access is not allowed
     */
    public void checkPermission(CachePermission permission, Subject subject) {
        permissionsChecker.checkPermission(permission, subject);
    }

    /**
     * This method performs the access control checks for the
     * specified <code>InvocationPermission</code> and <code>Subject</code>.
     * <p/>
     * If the check fails then a <code>java.lang.SecurityException</code>
     * will be thrown.
     * <p/>
     * <b>Note: </b>This method <i>must</i> be implemented to be thread safe.
     * <p/>
     *
     * @param permission The requested permission
     * @param subject    The subject accessing the cache
     * @throws SecurityException if access is not allowed
     */
    public void checkPermission(InvocationPermission permission, Subject subject) {
        permissionsChecker.checkPermission(permission, subject);
    }

    /**
     * @return the XML used to configure this access controller.
     */
    public XmlElement getConfig() {
        return xmlConfig;
    }

    /**
     * Configure this access controller using the specified XML.<p/>
     *
     * @param xmlConfig - the XML to use to configure this access controller
     */
    public void setConfig(XmlElement xmlConfig) {
        this.xmlConfig = (XmlElement) xmlConfig.clone();
        XmlElement xmlPermissions = xmlConfig.getSafeElement("permissions-checker");
        this.permissionsChecker = (PermissionsChecker) XmlHelper.createInstance(xmlPermissions, null, null);
    }
}